﻿using Avalonia.Animation;
using LightCAD.Core;
using LightCAD.Core.Element3d;
using LightCAD.Core.Elements;
using LightCAD.Drawing;
using LightCAD.Drawing.Actions;
using LightCAD.MathLib;
using LightCAD.Model;
using LightCAD.Runtime;
using SkiaSharp;
using static System.Collections.Specialized.BitVector32;
using SnapPointResult = LightCAD.Drawing.SnapPointResult;

namespace LightCAD.Component.Actions
{

    public class ExtrudeAction : ComponentInstance2dAction
    {
        private LcExtrudeInstance extrude;
        private LcExtrudeDef exDef;
        private double topScale = 1;
        private double bottomScale = 1;
        private Vector3 normal = new Vector3(0,0,1);
        private LcParameterSet parameterSet;
        private Vector2 insertPoint;
        private PointInputer PointInputer { get; set; }

        public ExtrudeAction() :base() 
        {
            
        }
        public ExtrudeAction(IDocumentEditor docEditor) : base(docEditor)
        {
            this.commandCtrl.WriteInfo("命令：Extrude"); 
        }
        public async void ExecCreate(string[] args = null)
        {
            this.StartCreating();
            var win = (IExtrudeDefWindow)AppRuntime.UISystem.CreateWindow("ExtrudeDefSelect");
            var result = AppRuntime.UISystem.ShowDialog(win);
            if (result != LcDialogResult.OK)
            {
                return;
            }
            topScale = win.TopScale;
            bottomScale = win.BottomScale;
            exDef = ComponentManager.GetCptDef<LcExtrudeDef>("拉伸体", "拉伸体", win.Uuid);
            if (exDef == null)
                return;
            extrude = new LcExtrudeInstance(10,topScale,bottomScale,normal ,exDef);
            PointInputer = new PointInputer(this.docEditor);
            Insert:
            var result0 = await PointInputer.Execute("请选择插入点");
            if (PointInputer.isCancelled)
            {
                goto End;
            }
            if (result0.ValueX!=null)
            {
                insertPoint = (Vector2)result0.ValueX;
            }
            else
            {
                var opvec = GetOptionVector(result0.Option);
                if (opvec != null)
                {
                    insertPoint = opvec;
                }else
                    goto Insert;
            }
            SetDepth:
            var result1= await PointInputer.Execute("请输入拉伸体高度:");
            if (PointInputer.isCancelled)
            {
                extrude.Parameters.SetValue("Depth", 100.0);
                goto Create;
            }
            if (!string.IsNullOrEmpty(result1.Option) && double.TryParse(result1.Option,out var depth))
            {
                extrude.Parameters.SetValue("Depth",depth);
            }
            else
            {
                goto SetDepth;
            }
            Create:
            extrude.Initilize(this.docRt.Document);
            extrude.Position.Set(insertPoint.X, insertPoint.Y, 0);
            extrude.ResetCache();
            this.docRt.Document.ModelSpace.InsertElement(extrude);
            End:
            this.EndCreating();
        }
        private Vector2 GetOptionVector(string Option)
        {
            if (!string.IsNullOrEmpty(Option))
            {
                var xyz = Option.split(",");
                if (double.TryParse(xyz[0].Trim(), out var pX) && double.TryParse(xyz[1].Trim(), out var pY))
                {
                    return new Vector2(pX, pY);
                }
            }
            return null;
        }
        public List<Curve2d> Cursor = new List<Curve2d>() {
            new Line2d(new Vector2(20, 20), new Vector2(-20, 20)),
            new Line2d(new Vector2(-20, 20), new Vector2(-20, -20)),
            new Line2d(new Vector2(-20, -20), new Vector2(20, -20)),
            new Line2d(new Vector2(20, -20), new Vector2(20, 20)),
            new Line2d(new Vector2(10, 10), new Vector2(-10, 10)),
            new Line2d(new Vector2(-10, 10), new Vector2(-10, -10)),
            new Line2d(new Vector2(-10, -10), new Vector2(10, -10)),
            new Line2d(new Vector2(10, -10), new Vector2(10, 10)),
            new Line2d(new Vector2(50,0),new Vector2(-50,0)),
            new Line2d(new Vector2(0,50),new Vector2(0,-50)) 
        };

        private void DrawCursor(SKCanvas canvas, Vector2 wcs_mp)
        {
            var cursor = this.Cursor.Clone();
            foreach (var curve in cursor)
            {
                if (curve is Line2d line)
                {
                    line.Start.MultiplyScalar(1/this.vportRt.Viewport.Scale);
                    line.End.MultiplyScalar(1 / this.vportRt.Viewport.Scale);
                    line.Translate(wcs_mp);
                    var start = this.vportRt.ConvertWcsToScr(line.Start).ToSKPoint();
                    var end = this.vportRt.ConvertWcsToScr(line.End).ToSKPoint();
                    using (var elePen = new SKPaint { Color = SKColors.White, IsStroke = true })
                    {
                        canvas.DrawLine(start, end, elePen);
                    }
                }
                else if (curve is Arc2d arc)
                {
                    arc.Radius *= (1 / this.vportRt.Viewport.Scale);
                    arc.Translate(wcs_mp);
                    using (var elePen = new SKPaint { Color = SKColors.White, IsStroke = true })
                    {
                        Matrix3 matrix = Matrix3.GetTranslate(new Vector2(0, 0));
                        this.vportRt.DrawArc(arc, matrix, canvas, elePen);
                    }
                }
            }
        }
        public override void DrawAuxLines(SKCanvas canvas)
        {
            var mp = this.vportRt.PointerMovedPosition.ToVector2d();
            var wcs_mp = this.vportRt.ConvertScrToWcs(mp);
            var sk_p = this.vportRt.ConvertWcsToScr(wcs_mp).ToSKPoint();
            if (extrude != null)
            {
                if (insertPoint == null)
                    DrawCursor(canvas, wcs_mp);
                var curves = extrude.Curves.FirstOrDefault();
                if (curves == null)
                    return;
                foreach (var curve in curves.Curve2ds)
                {
                    if (curve is Line2d)
                    {
                        var line = curve.Clone().Translate(insertPoint ?? wcs_mp) as Line2d;
                        var start = this.vportRt.ConvertWcsToScr(line.Start).ToSKPoint();
                        var end = this.vportRt.ConvertWcsToScr(line.End).ToSKPoint();
                        using (var elePen = new SKPaint { Color = SKColors.Green, IsStroke = true })
                        {
                            canvas.DrawLine(start, end, elePen);
                        }
                    }
                    else if (curve is Arc2d)
                    {
                        var arc = curve.Clone().Translate(insertPoint ?? wcs_mp) as Arc2d;

                        using (var elePen = new SKPaint { Color = SKColors.Green, IsStroke = true })
                        {
                            Matrix3 matrix = Matrix3.GetTranslate(new Vector2(0, 0));
                            this.vportRt.DrawArc(arc, matrix, canvas, elePen);
                        }
                    }
                }
                
            }
 
        }

        public override SnapPointResult SnapPoint(SnapRuntime snapRt, LcElement element, Vector2 point, bool forRef, Vector2 PrePoint = null)
        {
            var maxDistance = vportRt.GetSnapMaxDistance();
            var extrudeInstance = element as LcExtrudeInstance;
            var sscur = SnapSettings.Current;
            var result = new SnapPointResult { Element = element };
            if (sscur.ObjectOn && extrudeInstance.Curves.Count>0)
            {
                foreach (var curveGroup in extrudeInstance.Curves)
                {
                    foreach (var curve in curveGroup.Curve2ds)
                    {
                        var firstP = curve.GetPoints(1).First();
                        if (Utils.Vec2EQ(firstP, point, maxDistance))
                        {
                            result.Point = firstP;
                            result.Name = "Start";
                            result.Curves.Add(new SnapRefCurve(SnapPointType.Endpoint, curve.Clone()));
                        }
                    }
                }
            }
            if (result.Point != null)
                return result;
            else
                return null;
        }
        public override List<PropertyObserver> GetPropertyObservers()
        {
            //return base.GetPropertyObservers();
            return new List<PropertyObserver>() {
 
             new PropertyObserver()
            {
                Name = "Depth",
                DisplayName = "高度",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as LcExtrudeInstance).Parameters.GetValue<double>("Depth"),
                Setter = (ele, value) =>
                {
                    var extrudeInstance = (ele as LcExtrudeInstance);
                    if (!double.TryParse(value.ToString(),out var depth)||depth<0)
                        return;
                    extrudeInstance.OnPropertyChangedBefore("Depth",extrudeInstance.Parameters.GetValue<double>("Depth"),depth);
                    extrudeInstance.Parameters.SetValue("Depth",depth );
                    extrudeInstance.ResetCache();
                    extrudeInstance.OnPropertyChangedAfter("Depth",extrudeInstance.Parameters.GetValue<double>("Depth"),depth);
                }
            },
              new PropertyObserver()
            {
                Name = "TopScale",
                DisplayName = "顶部放大比例",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as LcExtrudeInstance).Parameters.GetValue<double>("TopScale"),
                Setter = (ele, value) =>
                {
                    var extrudeInstance = (ele as LcExtrudeInstance);
                    if (!double.TryParse(value.ToString(),out var topScale)||topScale<0)
                        return;
                    extrudeInstance.OnPropertyChangedBefore("TopScale",extrudeInstance.Parameters.GetValue<double>("TopScale"),topScale);
                    extrudeInstance.Parameters.SetValue("TopScale",topScale );
                    extrudeInstance.ResetCache();
                    extrudeInstance.OnPropertyChangedAfter("TopScale",extrudeInstance.Parameters.GetValue<double>("TopScale"),topScale);
                }
            }
              ,
              new PropertyObserver()
            {
                Name = "BottomScale",
                DisplayName = "底部放大比例",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as LcExtrudeInstance).Parameters.GetValue<double>("BottomScale"),
                Setter = (ele, value) =>
                {
                    var extrudeInstance = (ele as LcExtrudeInstance);
                    if (!double.TryParse(value.ToString(),out var bottomScale)||bottomScale<0)
                        return;
                    extrudeInstance.OnPropertyChangedBefore("BottomScale",extrudeInstance.Parameters.GetValue<double>("BottomScale"),bottomScale);
                    extrudeInstance.Parameters.SetValue("BottomScale",bottomScale );
                    extrudeInstance.ResetCache();
                    extrudeInstance.OnPropertyChangedAfter("BottomScale",extrudeInstance.Parameters.GetValue<double>("BottomScale"),bottomScale);
                }
            }
            };
        }
    }
    public class Extrude3dAction : ComponentInstance3dAction
    {
        private LcExtrudeInstance extrude;
        private LcExtrudeDef exDef;
        private double topScale = 1;
        private double bottomScale = 1;
        public Extrude3dAction(IDocumentEditor docEditor) : base(docEditor)
        {
            this.commandCtrl.WriteInfo("命令：Extrude");
        }
        public Extrude3dAction( ) : base( )
        {
         }
        public async void ExecCreate(string[] args = null)
        {
            //var mats = new LcMaterial[3]
            //    {
            //    new LcMaterial() { Color = new Color(0xff0000) },
            //    new LcMaterial() { Color = new Color(0x00ff00) },
            //    new LcMaterial() { Color = new Color(0x0000ff) }
            //    };
            //var result  = this.docRt.Model3DEditRt.InvokeMethod("ShowSections");
            //if (result==null)
            //    return;
            var win = (IExtrudeDefWindow)AppRuntime.UISystem.CreateWindow("ExtrudeDefSelect");
            var result = AppRuntime.UISystem.ShowDialog(win);
            if (result != LcDialogResult.OK)
            {
                return;
            }
            topScale = win.TopScale;
            bottomScale = win.BottomScale;
            exDef = ComponentManager.GetCptDef<LcExtrudeDef>("拉伸体", "拉伸体", win.Uuid);
            if (exDef == null)
                return;
            StartAction();
            var profileNormal = new Vector3(0, 0, 1);
            Vector3 start = null;
            Vector3 end = null;
            PointInputer3d pointer = new PointInputer3d(docEditor);
            bool startPush = false;
            this.model3dRuntime.EnableCameraControl(false);
            this.snap3dRuntime.AddSnapPlanes(new ListEx<Plane>
            {
                new Plane().SetFromNormalAndCoplanarPoint(new Vector3(0, 0, 1), new Vector3())
            });
            void onMouseEvent(string name, MouseEventRuntime mer)
            {
                if (this.snap3dRuntime.SnapPoint == null)
                    return;
                if (name == "Move")
                {
                    if (start == null)
                    {
                        this.CreateOrUpdateExtrude(this.snap3dRuntime.SnapPoint.Clone(), this.snap3dRuntime.SnapPoint.Clone().AddScaledVector(new Vector3(0, 0, 1), 10));
                        return;
                    }
                    else
                    {
                        if (extrude != null && this.snap3dRuntime.SnapPoint != null)
                        {
                            Move(this.snap3dRuntime.SnapPoint);
                        }
                    }
                }
                else if (name == "Down")
                {
                    if (start == null)
                    {
                        start = this.snap3dRuntime.SnapPoint.Clone();
                        this.snap3dRuntime.ClearSnapPlanes();
                        var camera = this.snap3dRuntime.GetCamera();
                        var yAxis = camera.getWorldDirection().Negate().Cross(profileNormal).Normalize();
                        var zAxis = profileNormal.Clone().Cross(yAxis).Normalize();
                        this.snap3dRuntime.AddSnapPlanes(new ListEx<Plane>
                        {
                            new Plane().SetFromNormalAndCoplanarPoint(zAxis,this.snap3dRuntime.SnapPoint.Clone())
                        });
                    }
                    else
                    {
                        this.snap3dRuntime.EndSnaping();
                        this.model3dRuntime.Control.DetachEvents(null, null, onMouseEvent, null, null, null, null);
                        this.snap3dRuntime.ClearSnapPlanes();
                        this.model3dRuntime.EnableCameraControl(true);
                    }
                }
            }

            void Move(Vector3 target)
            {
                CreateOrUpdateExtrude(start, target.Clone());
            }
            this.snap3dRuntime.StartSnaping(SnapFilterType.OnlyPlane);
            this.model3dRuntime.Control.AttachEvents(null, null, onMouseEvent, null, null, null, null);
            EndAction();

        }
        private void CreateOrUpdateExtrude(Vector3 start, Vector3 end)
        {
            if (this.extrude != null)
            {
                this.docRt.Document.ModelSpace.RemoveElement(this.extrude);
            }
            var mats = new LcMaterial[3]
            {
                new LcMaterial() { Color = new Color(0xff0000) },
                new LcMaterial() { Color = new Color(0x00ff00) },
                new LcMaterial() { Color = new Color(0x0000ff) },
            };

            var normal = end.Clone().Sub(start).Normalize();
            extrude = new LcExtrudeInstance(end.DistanceTo(start), topScale, bottomScale, normal, exDef);
            extrude.Initilize(this.docRt.Document);
            (extrude.Rt3DAction as IComponentAction).SetDocEditor(this.docEditor);
            extrude.Position.Set(start.X, start.Y, start.Z);
            this.docRt.Document.ModelSpace.InsertElement(extrude);
        }
    }


}
